<template>
    <div class="jvm-info">
        <div class="alert">
            <el-alert :closable="false" :show-icon="true" type="success">
                数据获取时间 {{ datetime }}
                <a style="position: absolute; right: 35px; cursor: pointer" @click="onRefresh">点击刷新</a>
            </el-alert>
        </div>
        <el-card :body-style="{ padding: '10px' }" shadow="hover">
            <div id="memoryInfo" class="chart-box"></div>
        </el-card>
        <el-table :data="dataSource" style="margin-top: 5px">
            <el-table-column label="参数" prop="name"></el-table-column>
            <el-table-column label="描述" prop="description"></el-table-column>
            <el-table-column label="当前值" prop="value"></el-table-column>
        </el-table>
    </div>
</template>
<script>
import axios from 'axios';
import {service, dateFormat} from "@/utils";
import * as echarts from 'echarts';

import {defineComponent, getCurrentInstance, onBeforeUnmount, onMounted, reactive, ref} from 'vue';

export default defineComponent({
    setup() {
        const {proxy} = getCurrentInstance();

        const dataSource = ref([]);
        const datetime = ref([]);
        const memory = reactive({
            instance: null,
            data: [],
            xdata: [],
            option: {
                title: {
                    text: 'JVM内存使用情况'
                },
                tooltip: {
                    trigger: 'axis',
                    formatter: function (params) {
                        return params[0].value;
                    },
                    axisPointer: {
                        type: 'cross'
                    }
                },

                xAxis: {
                    type: 'category',
                    splitLine: {
                        show: false
                    },
                    data: null
                },
                yAxis: {
                    type: 'value',
                    boundaryGap: [0, '100%'],
                    axisLabel: {
                        formatter: '{value} MB'
                    },
                    splitLine: {
                        show: false
                    }
                },
                dataZoom: [
                    {
                        type: 'inside',
                        start: 0,
                        end: 30
                    },
                    {
                        start: 0,
                        end: 30
                    }
                ],
                series: [{
                    name: '模拟数据',
                    type: 'line',
                    showSymbol: false,
                    itemStyle: {
                        color: 'rgb(255, 70, 131)'
                    },
                    data: []
                }]
            }
        });

        const convert = (value) => {
            return Number(value / 1048576).toFixed(3);
        };
        const desc = reactive({
            'jvm.memory.max': {desc: 'JVM 最大内存', unit: 'MB', convert: convert},
            'jvm.memory.committed': {desc: 'JVM 可用内存', unit: 'MB', convert: convert},
            'jvm.memory.used': {desc: 'JVM 已用内存', unit: 'MB', convert: convert},
            'jvm.buffer.memory.used': {desc: 'JVM 缓冲区已用内存', unit: 'MB', convert: convert},
            'jvm.buffer.count': {desc: '当前缓冲区数量', unit: '个'},
            'jvm.threads.daemon': {desc: 'JVM 守护线程数量', unit: '个'},
            'jvm.threads.live': {desc: 'JVM 当前活跃线程数量', unit: '个'},
            'jvm.threads.peak': {desc: 'JVM 峰值线程数量', unit: '个'},
            'jvm.classes.loaded': {desc: 'JVM 已加载 Class 数量', unit: '个'},
            'jvm.classes.unloaded.count': {desc: 'JVM 未加载 Class 数量', unit: '个'},
            'jvm.gc.memory.allocated.count': {desc: 'GC 时, 年轻代分配的内存空间', unit: 'MB', convert: convert},
            'jvm.gc.memory.promoted.count': {desc: 'GC 时, 老年代分配的内存空间', unit: 'MB', convert: convert},
            'jvm.gc.max.data.size': {desc: 'GC 时, 老年代的最大内存空间', unit: 'MB', convert: convert},
            'jvm.gc.live.data.size': {desc: 'FullGC 时, 老年代的内存空间', unit: 'MB', convert: convert},
            'jvm.gc.pause.count': {desc: '系统启动以来GC 次数', unit: '次'},
            'jvm.gc.pause.totalTime': {desc: '系统启动以来GC 总耗时', unit: '秒'},
            'jvm.gc.pause.max': {desc: '系统启动以来GC 最大耗时', unit: '秒'}
        });

        const onRefresh = () => {
            let date = new Date();
            datetime.value = date.getFullYear() + '年' + date.getMonth() + '月' + date.getDay() + '日' +
                    date.getHours() + '时' + date.getMinutes() + '分' + date.getSeconds() + '秒';

            axios.all([
                service.get('/actuator/metrics/jvm.memory.max'),
                service.get('/actuator/metrics/jvm.memory.committed'),
                service.get('/actuator/metrics/jvm.memory.used'),
                service.get('/actuator/metrics/jvm.buffer.memory.used'),
                service.get('/actuator/metrics/jvm.buffer.count'),
                service.get('/actuator/metrics/jvm.threads.daemon'),
                service.get('/actuator/metrics/jvm.threads.live'),
                service.get('/actuator/metrics/jvm.threads.peak'),
                service.get('/actuator/metrics/jvm.classes.loaded'),
                service.get('/actuator/metrics/jvm.classes.unloaded'),
                service.get('/actuator/metrics/jvm.gc.memory.allocated'),
                service.get('/actuator/metrics/jvm.gc.memory.promoted'),
                service.get('/actuator/metrics/jvm.gc.max.data.size'),
                service.get('/actuator/metrics/jvm.gc.live.data.size'),
                service.get('/actuator/metrics/jvm.gc.pause')
            ]).then((r) => {
                dataSource.value = [];
                r.forEach(item => {
                    for (let i = 0; i < item.measurements.length; i++) {
                        let obj = {};
                        let measurement = item.measurements[i];
                        obj.name = measurement.statistic === 'VALUE' ? item.name : item.name + '.' + measurement.statistic.toLowerCase().toCamelCase();
                        let dc = desc[obj.name];
                        obj.value = (dc.convert ? dc.convert(measurement.value) : measurement.value) + (dc.unit || '');
                        obj.description = dc.desc;
                        dataSource.value.push(obj);
                    }
                });
            }).catch((r) => {
                console.error(r);
                proxy.$message.error('获取JVM信息失败');
            });
        };

        let interval = null;
        const initEchartsHandle = () => {
            let minMemory = 1e10;
            let maxMemory = -1e10;
            interval = setInterval(() => {
                axios.all([service.get('/actuator/metrics/jvm.memory.used')]).then((r) => {
                    let currentMemory = Number(convert(r[0].measurements[0].value)).toFixed(2);
                    if (currentMemory < minMemory) minMemory = currentMemory;
                    if (currentMemory > maxMemory) maxMemory = currentMemory;
                    let time = dateFormat(new Date(), 'mm:ss');
                    memory.data.push(currentMemory);
                    memory.xdata.push(time);
                    if (memory.data.length >= 300) {
                        memory.data.shift();
                        memory.xdata.shift();
                    }

                    memory.instance.setOption({
                        series: [{
                            data: memory.data
                        }],
                        xAxis: {
                            data: memory.xdata
                        }
                    });

                }).catch((r) => {
                    console.error(r);
                    stop();
                });
            }, 1000);
        };

        const stop = () => {
            if (interval) {
                clearInterval(interval);
                interval = null;
            }
        };

        onMounted(() => {
            memory.instance = echarts.init(document.getElementById('memoryInfo'));
            memory.instance.setOption(memory.option);
            initEchartsHandle();
            onRefresh();
        });

        onBeforeUnmount(() => stop());

        return {
            datetime, dataSource,
            onRefresh

        };
    }
});
</script>
<style lang="scss" scoped>
.jvm-info {
    width: 100%;

    .chart-box {
        min-height: 40vh;
    }
}
</style>
